package top.glike.ssl.auto.service.impl;

import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.glike.ssl.auto.config.SshServerConfig;
import top.glike.ssl.auto.domain.other.AutoSslDomain;
import top.glike.ssl.auto.utils.CommandResult;
import top.glike.ssl.auto.utils.FileUploadResult;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * packageName top.glike.ssl.auto.service.impl
 *
 * @author Sophia
 * @className CertificateDeploymentService
 * @description TODO
 * @date 2025/10/13
 */
@Slf4j
@Service
public class CertificateDeploymentService {

    /**
     * 部署SSL证书到服务器
     */
    public boolean deployCertificateToServer(SshServerConfig serverConfig,
                                             String certLocalPath,
                                             String keyLocalPath,
                                             String remoteCertDir) {
        /*try {
            // 1. 上传证书文件
            FileUploadResult certUpload = sshService.uploadFile(
                    serverConfig, certLocalPath, remoteCertDir, "domain.crt");

            FileUploadResult keyUpload = sshService.uploadFile(
                    serverConfig, keyLocalPath, remoteCertDir, "domain.key");

            if (!certUpload.isSuccess() || !keyUpload.isSuccess()) {
                log.error("Certificate file upload failed");
                return false;
            }

            // 2. 设置正确的文件权限（证书文件通常需要限制访问权限）
            String chmodCommand = String.format("chmod 600 %s/domain.key", remoteCertDir);
            CommandResult chmodResult = sshService.executeCommand(serverConfig, chmodCommand);
            if (!chmodResult.isSuccess()) {
                log.warn("Failed to set key file permissions: {}", chmodResult.getError());
            }

            // 3. 重新加载Nginx配置
            CommandResult reloadResult = sshService.executeCommand(serverConfig, "nginx -s reload");
            if (!reloadResult.isSuccess()) {
                log.error("Failed to reload nginx: {}", reloadResult.getError());
                return false;
            }

            log.info("Certificate deployed successfully to {}", serverConfig.getHost());
            return true;

        } catch (Exception e) {
            log.error("Certificate deployment failed", e);
            return false;
        }*/
        return true;
    }

    /**
     * 执行证书更新脚本
     */
    public boolean executeCertificateUpdateScript(SshServerConfig serverConfig,
                                                  String scriptLocalPath,
                                                  String domain,
                                                  String certPath,
                                                  String keyPath) {

        /*String remoteScriptDir = "/tmp/cert-scripts";
        String scriptName = "update_cert.sh";

        return sshService.uploadAndExecuteScript(
                serverConfig,
                scriptLocalPath,
                remoteScriptDir,
                scriptName,
                domain,
                certPath,
                keyPath
        );*/
        return false;
    }


    /**
     * 上传文件到远程服务器
     */
    public FileUploadResult uploadFile(AutoSslDomain sslDomain) {
        Session session = null;
        ChannelSftp channelSftp = null;
        long startTime = System.currentTimeMillis();

        try {
            // 创建会话并连接
            session = createSession(sslDomain);
            session.connect();

            // 创建SFTP通道
            channelSftp = (ChannelSftp) session.openChannel("sftp");
            channelSftp.connect();

            // 检查并创建远程目录
            createRemoteDirectory(channelSftp, sslDomain.getCertServerPath());

            // 构建远程文件路径
            String remoteFilePath = sslDomain.getCertServerPath();

            // 获取本地文件信息
            Path localPath = Paths.get(sslDomain.getCertFile());
            long fileSize = Files.size(localPath);

            // 上传文件
            log.info("Uploading file: {} to {}", sslDomain.getCertFile(), remoteFilePath);
            channelSftp.put(sslDomain.getCertFile(), remoteFilePath);
            channelSftp.put(sslDomain.getKeyFile(), remoteFilePath);

            long uploadTime = System.currentTimeMillis() - startTime;
            log.info("File uploaded successfully in {} ms", uploadTime);

            return FileUploadResult.success(remoteFilePath, fileSize, uploadTime);

        } catch (Exception e) {
            log.error("File upload failed", e);
            return FileUploadResult.failure("Upload failed: " + e.getMessage());
        } finally {
            // 关闭连接
            closeConnection(channelSftp, session);
        }
    }


    /**
     * 关闭SSH连接
     */
    private void closeConnection(Channel channel, Session session) {
        try {
            if (channel != null && channel.isConnected()) {
                channel.disconnect();
            }
        } catch (Exception e) {
            log.warn("Error closing channel", e);
        }

        try {
            if (session != null && session.isConnected()) {
                session.disconnect();
            }
        } catch (Exception e) {
            log.warn("Error closing session", e);
        }
    }

    /**
     * 创建SSH会话
     *
     * @param sslDomain
     * @return
     * @throws JSchException
     */
    public Session createSession(AutoSslDomain sslDomain) throws JSchException {
        JSch jsch = new JSch();
        Session session = jsch.getSession(sslDomain.getUsername(), sslDomain.getIpAddress(), Math.toIntExact(sslDomain.getSshPort()));
        // 使用密码认证
        session.setPassword(sslDomain.getPassword());

        // 配置SSH连接参数
        java.util.Properties sshConfig = new java.util.Properties();
        sshConfig.put("StrictHostKeyChecking", "no"); // 接受未知主机密钥（生产环境应设为"yes"）
        sshConfig.put("PreferredAuthentications", "publickey,password");

        session.setConfig(sshConfig);
        session.setTimeout(1000);

        return session;
    }

    /**
     * 创建远程目录（如果不存在）
     */
    private void createRemoteDirectory(ChannelSftp channelSftp, String remoteDirectory) throws SftpException {
        try {
            channelSftp.stat(remoteDirectory);
            log.debug("Remote directory already exists: {}", remoteDirectory);
        } catch (SftpException e) {
            if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
                // 目录不存在，递归创建
                String[] directories = remoteDirectory.split("/");
                StringBuilder path = new StringBuilder();

                for (String dir : directories) {
                    if (!dir.isEmpty()) {
                        if (path.length() > 0) path.append("/");
                        path.append(dir);
                        try {
                            channelSftp.mkdir(path.toString());
                            log.debug("Created remote directory: {}", path);
                        } catch (SftpException e2) {
                            // 目录可能已存在，忽略这个错误
                            if (e2.id != ChannelSftp.SSH_FX_FAILURE) {
                                throw e2;
                            }
                        }
                    }
                }
                log.info("Created remote directory: {}", remoteDirectory);
            } else {
                throw e;
            }
        }
    }

    /**
     * 执行Shell命令
     */
    public CommandResult executeCommand(AutoSslDomain sslDomain, String command) {
        Session session = null;
        ChannelExec channelExec = null;
        long startTime = System.currentTimeMillis();

        try {
            // 创建会话并连接
            session = createSession(sslDomain);
            session.connect();

            // 创建执行通道
            channelExec = (ChannelExec) session.openChannel("exec");
            channelExec.setCommand(command);

            // 获取输入输出流
            InputStream in = channelExec.getInputStream();
            InputStream err = channelExec.getErrStream();

            // 执行命令
            channelExec.connect();

            // 读取标准输出
            String output = readStream(in);
            String error = readStream(err);

            // 等待命令执行完成
            while (!channelExec.isClosed()) {
                Thread.sleep(100);
            }

            int exitStatus = channelExec.getExitStatus();
            long executionTime = System.currentTimeMillis() - startTime;

            log.info("Command executed: {}, Exit status: {}, Time: {} ms",
                    command, exitStatus, executionTime);

            if (exitStatus == 0) {
                return CommandResult.success(output, executionTime);
            } else {
                return CommandResult.failure(error, exitStatus, executionTime);
            }

        } catch (Exception e) {
            log.error("Command execution failed: {}", command, e);
            long executionTime = System.currentTimeMillis() - startTime;
            return CommandResult.failure("Execution failed: " + e.getMessage(), -1, executionTime);
        } finally {
            closeConnection(channelExec, session);
        }
    }

    /**
     * 读取输入流内容
     */
    private String readStream(InputStream in) throws IOException {
        StringBuilder content = new StringBuilder();
        byte[] buffer = new byte[1024];
        int bytesRead;

        while ((bytesRead = in.read(buffer)) != -1) {
            content.append(new String(buffer, 0, bytesRead));
        }

        return content.toString().trim();
    }

    /**
     * 构建脚本执行命令
     */
    private String buildScriptCommand(String scriptPath, String... args) {
        StringBuilder command = new StringBuilder(scriptPath);

        for (String arg : args) {
            command.append(" ").append(escapeArgument(arg));
        }

        return command.toString();
    }

    /**
     * 转义命令行参数
     */
    private String escapeArgument(String arg) {
        if (arg == null) return "";
        return "'" + arg.replace("'", "'\\''") + "'";
    }

}
